/*
 * Copyright  2017-2019 NXP
 * All rights reserved.
 *
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include "fsl_common.h"
#include "fsl_elcdif.h"
#include "fsl_debug_console.h"

#include "fsl_lpuart.h"
#include "tos_k.h"
#include "tos_at.h"

#include "fsl_gpio.h"
#include "pin_mux.h"
#include "clock_config.h"
#include "board.h"

#include "configs.h"
#include "atk_lora_01.h"
#include "font.h"
#include "utils.h"
#include "graphic.h"
#include "axis.h"
#include "history.h"
#include "buttons.h"
#include "button_ctrl.h"
#include "lcd_ctrl.h"
/*******************************************************************************
 * Definitions
 ******************************************************************************/
#define APP_ELCDIF LCDIF
#define APP_POL_FLAGS \
    (kELCDIF_DataEnableActiveHigh | kELCDIF_VsyncActiveLow | kELCDIF_HsyncActiveLow | kELCDIF_DriveDataOnRisingClkEdge)

/* Display. */
#define LCD_DISP_GPIO     GPIO1
#define LCD_DISP_GPIO_PIN 2
/* Back light. */
#define LCD_BL_GPIO     GPIO2
#define LCD_BL_GPIO_PIN 31

/* Frame buffer data alignment, for better performance, the LCDIF frame buffer should be 64B align. */
#define FRAME_BUFFER_ALIGN 64

#ifndef APP_LCDIF_DATA_BUS
#define APP_LCDIF_DATA_BUS kELCDIF_DataBus24Bit
#endif

#define F1_GPIO         GPIO4
#define F1_GPIO_PIN     (20U)
#define F1_SW_NAME      "SW_S1"
#define F1_GPIO_IRQ     GPIO1_Combined_0_15_IRQn

#define F2_GPIO         GPIO1
#define F2_GPIO_PIN     (11U)
#define F2_SW_NAME      "SW_S2"
#define F2_GPIO_IRQ     GPIO4_Combined_16_31_IRQn

/*******************************************************************************
 * Prototypes
 ******************************************************************************/
void BOARD_EnableLcdInterrupt(void);

/*******************************************************************************
 * Variables
 ******************************************************************************/
static volatile bool s_frameDone = false;

AT_NONCACHEABLE_SECTION_ALIGN(static uint32_t s_frameBuffer[2][APP_IMG_HEIGHT][APP_IMG_WIDTH], FRAME_BUFFER_ALIGN);

/*******************************************************************************
 * Code
 ******************************************************************************/
void GPIO1_Combined_0_15_IRQHandler(void)
{
    /* clear the interrupt status */
    GPIO_PortClearInterruptFlags(F2_GPIO, 1U << F2_GPIO_PIN);
    /* Change state of switch. */
	buttons_set_values(0 == GPIO_PinRead(F1_GPIO, F1_GPIO_PIN) ? BTN_STATUS_PRESSED : BTN_STATUS_RELEASE,
		0 == GPIO_PinRead(F2_GPIO, F2_GPIO_PIN) ? BTN_STATUS_PRESSED : BTN_STATUS_RELEASE);
    SDK_ISR_EXIT_BARRIER;
}

void GPIO4_Combined_16_31_IRQHandler(void)
{
    /* clear the interrupt status */
    GPIO_PortClearInterruptFlags(F1_GPIO, 1U << F1_GPIO_PIN);
    /* Change state of switch. */
	buttons_set_values(0 == GPIO_PinRead(F1_GPIO, F1_GPIO_PIN) ? BTN_STATUS_PRESSED : BTN_STATUS_RELEASE,
		0 == GPIO_PinRead(F2_GPIO, F2_GPIO_PIN) ? BTN_STATUS_PRESSED : BTN_STATUS_RELEASE);
    SDK_ISR_EXIT_BARRIER;
}

void LPUART2_IRQHandler(void)
{
	// Receive a byte from uart2
	uint8_t data;
	if ((kLPUART_RxDataRegFullFlag) & LPUART_GetStatusFlags(LPUART2))
	{
		data = LPUART_ReadByte(LPUART2);
		if (tos_knl_is_running())
		{
			tos_knl_irq_enter();
			tos_at_uart_input_byte(data);
			tos_knl_irq_leave();
		}
	}
}

void SysTick_Handler(void)
{
	if (tos_knl_is_running())
	{
		tos_knl_irq_enter();
		tos_tick_handler();
		tos_knl_irq_leave();
	}
}


extern void APP_LCDIF_IRQHandler(void);

void LCDIF_IRQHandler(void)
{
    APP_LCDIF_IRQHandler();
    __DSB();
}

/* Enable interrupt. */
void BOARD_EnableLcdInterrupt(void)
{
    EnableIRQ(LCDIF_IRQn);
}

/* Initialize the LCD_DISP. */
void BOARD_InitLcd(void)
{
    gpio_pin_config_t config = {
        kGPIO_DigitalOutput,
        0,
        kGPIO_NoIntmode,
    };

    /* Backlight. */
    config.outputLogic = 1;
    GPIO_PinInit(LCD_BL_GPIO, LCD_BL_GPIO_PIN, &config);
}

void BOARD_InitLcdifPixelClock(void)
{
    /*
     * The desired output frame rate is 60Hz. So the pixel clock frequency is:
     * (480 + 4 + 4 + 4) * (272 + 30 + 210 + 30) * 60 = 31.5M.
     * Here set the LCDIF pixel clock to 31M.
     */

    /*
     * Initialize the Video PLL.
     * Video PLL output clock is OSC24M * (loopDivider + (denominator / numerator)) / postDivider = 93MHz.
     */
    clock_video_pll_config_t config = {
        .loopDivider = 31,
        .postDivider = 8,
        .numerator   = 0,
        .denominator = 0,
    };

    CLOCK_InitVideoPll(&config);

    /*
     * 000 derive clock from PLL2
     * 001 derive clock from PLL3 PFD3
     * 010 derive clock from PLL5
     * 011 derive clock from PLL2 PFD0
     * 100 derive clock from PLL2 PFD1
     * 101 derive clock from PLL3 PFD1
     */
    CLOCK_SetMux(kCLOCK_LcdifPreMux, 2);

    CLOCK_SetDiv(kCLOCK_LcdifPreDiv, 0);

    CLOCK_SetDiv(kCLOCK_LcdifDiv, 2);
}


void APP_LCDIF_IRQHandler(void)
{
    uint32_t intStatus;

    intStatus = ELCDIF_GetInterruptStatus(APP_ELCDIF);

    ELCDIF_ClearInterruptStatus(APP_ELCDIF, intStatus);

    if (intStatus & kELCDIF_CurFrameDone)
    {
        s_frameDone = true;
    }
    SDK_ISR_EXIT_BARRIER;
}

void APP_ELCDIF_Init(void)
{
    const elcdif_rgb_mode_config_t config = {
        .panelWidth    = APP_IMG_WIDTH,
        .panelHeight   = APP_IMG_HEIGHT,
        .hsw           = APP_HSW,
        .hfp           = APP_HFP,
        .hbp           = APP_HBP,
        .vsw           = APP_VSW,
        .vfp           = APP_VFP,
        .vbp           = APP_VBP,
        .polarityFlags = APP_POL_FLAGS,
        .bufferAddr    = (uint32_t)s_frameBuffer[0],
        .pixelFormat   = kELCDIF_PixelFormatXRGB8888,
        .dataBus       = APP_LCDIF_DATA_BUS,
    };

#if (defined(APP_ELCDIF_HAS_DISPLAY_INTERFACE) && APP_ELCDIF_HAS_DISPLAY_INTERFACE)
    BOARD_InitDisplayInterface();
#endif
    ELCDIF_RgbModeInit(APP_ELCDIF, &config);
}


void APP_FillFrameBuffer(uint32_t frameBuffer[APP_IMG_HEIGHT][APP_IMG_WIDTH])
{
    // Update history
    // history_put(get_sys_time(), get_current_elec_value());

	// Draw framebuffer
	lcd_ctrl_fill_framebuffer(frameBuffer);
}

#define TASK1_STK_SIZE 1024
k_task_t task1;
uint8_t task1_stk[TASK1_STK_SIZE];

static void on_handle_elec_event(const char * prefix, const char * data, size_t size)
{
	// PRINTF("%s %d Prefix: %s data[%d] %s\n", __FUNCTION__, __LINE__, prefix, size, data);
	char data_[16] = {0};
	memcpy(data_, data, size);  // In case buffer overflow
	// add_elec_value(atoi(data_));
    history_put(get_sys_time(), atoi(data_));
}

static int time_sync = 0;

static void on_handle_time_sync(const char * prefix, const char * data, size_t size)
{
	PRINTF("%s %d Prefix: %s data[%d] %s\n", __FUNCTION__, __LINE__, prefix, size, data);
	char data_[16] = {0};
	for (size_t i = 0, j = 0; i < size; ++i)
	{
		if (data[i] >= '0' && data[i] <= '9')
		{
			data_[j++] = data[i];
		}
	}
	data_[sizeof(data_) - 1] = '\0';
	k_tick_t t = str_2_k_tick_t(data_);
	set_sys_time(t);
	update_elec_time_begin(&t);
	time_sync = 1;
}

static atk_lora_recv_event_t atk_lora_events[] = {
		{ "+ELEC:", on_handle_elec_event, 2000 },
		{ "+TIMESYNC:", on_handle_time_sync, 2000 }
};

void task1_entry(void * arg)
{
    atk_lora_lora_init(HAL_UART_PORT_2, atk_lora_events,
            sizeof(atk_lora_events) / sizeof(atk_lora_events[0]));
    {
		char recv_buf[128] = {0};
		atk_lora_send("AT+TIMESYNC?", strlen("AT+TIMESYNC?"), K_NULL, recv_buf, sizeof(recv_buf));
    }
	while (!time_sync)
	{
		tos_task_delay(20);
	}
    history_init();
    history_update_simulate_data();
	btn_ctrl_init();  // Set here to prevent handling the button interrupts very early
	atk_lora_send("AT+UPLOADSTART", strlen("AT+UPLOADSTART"), K_NULL, K_NULL, 0);
    while (1)
    {
        history_process();
        tos_task_delay(20);
    }
    history_deinit();
}

#define TASK2_STK_SIZE 2048
k_task_t task2;
uint8_t task2_stk[TASK2_STK_SIZE];

void task2_entry(void * arg)
{
    uint32_t frameBufferIndex = 0;

    /* Clear the frame buffer. */
    memset(s_frameBuffer, 0, sizeof(s_frameBuffer));

    APP_FillFrameBuffer(s_frameBuffer[frameBufferIndex]);

    ELCDIF_EnableInterrupts(APP_ELCDIF, kELCDIF_CurFrameDoneInterruptEnable);
    ELCDIF_RgbModeStart(APP_ELCDIF);

    while (1)
    {
        frameBufferIndex ^= 1U;

        APP_FillFrameBuffer(s_frameBuffer[frameBufferIndex]);

        ELCDIF_SetNextBufferAddr(APP_ELCDIF, (uint32_t)s_frameBuffer[frameBufferIndex]);

		k_tick_t t_begin = get_sys_time();
        s_frameDone = false;
        /* Wait for previous frame complete. */
        while (!s_frameDone)
        {
        	tos_task_delay(20);
        }

		while ((get_sys_time() - t_begin) < CONF_LCD_REFRESH_INTERVAL && !is_force_refresh_lcd())
		{
			tos_task_delay(20);
		}
		if (is_force_refresh_lcd())
		{
			clear_force_refresh_lcd();
		}
    }
}

/*!
 * @brief Main function
 */
int main(void)
{
    /* Define the init structure for the input switch pin */
    gpio_pin_config_t sw_config = {
        kGPIO_DigitalInput,
        0,
		kGPIO_IntRisingOrFallingEdge,
    };

    BOARD_ConfigMPU();
    BOARD_InitPins();
 //   BOARD_InitSemcPins();
    BOARD_BootClockRUN();
    BOARD_InitLcdifPixelClock();
    BOARD_InitDebugConsole();
    BOARD_InitLcd();

    PRINTF("MCX LCDIF RGB example start...\r\n");

    /* Init input switch GPIO. */
    EnableIRQ(F1_GPIO_IRQ);
    EnableIRQ(F2_GPIO_IRQ);
    GPIO_PinInit(F1_GPIO, F1_GPIO_PIN, &sw_config);
    GPIO_PinInit(F2_GPIO, F2_GPIO_PIN, &sw_config);

    /* Enable GPIO pin interrupt */
    GPIO_PortEnableInterrupts(F1_GPIO, 1U << F1_GPIO_PIN);
    GPIO_PortEnableInterrupts(F2_GPIO, 1U << F2_GPIO_PIN);

    APP_ELCDIF_Init();

    BOARD_EnableLcdInterrupt();

	tos_knl_init();
	lcd_ctrl_init();
	tos_task_create(&task1, "task1", task1_entry, NULL, 4, task1_stk, TASK1_STK_SIZE, 0);  // Create task 1
	tos_task_create(&task2, "task2", task2_entry, NULL, 3, task2_stk, TASK2_STK_SIZE, 0);  // Create task 2
	tos_knl_start();

	lcd_ctrl_deinit();
}
